package com.zhou.core.os.v3_oshi;

import com.alibaba.fastjson.JSON;
import com.zhou.core.os.v1.OSService;
import com.zhou.core.os.v3_oshi.entity.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oshi.PlatformEnum;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.software.os.OSProcess;
import oshi.util.FormatUtil;
import oshi.util.Util;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 *
 */
public class OSHIService {
    private static final int OSHI_WAIT_SECOND = 1000;
    public static final String UNIT_K = "K";
    public static final String UNIT_M = "M";
    public static final String UNIT_G = "G";
    public static final long DESK_UNIT_G = 1024 * 1024l;
    public static final long MEM_UNIT_M_VALUE = 1024 * 1024l;
    public static final int PER_UNIT = 100;
    private static Logger logger  = LoggerFactory.getLogger(OSHIService.class);

    static OSHIService instance = new OSHIService();
    public static OSHIService getInstance()
    {
        return instance;
    }
    private static Runtime r = Runtime.getRuntime();
    private  SystemInfo systemInfo  = new SystemInfo();



   public  String getInet4Ip()
   {

       return OSService.getInet4Ip();
   }

    /**
     * 获取系统数据
     * @return
     */
   public OSHISystem generateOSHISystem()
   {
       OSHISystem result = new OSHISystem();
       result.setInet4Ip(getInet4Ip());
       result.setMemory(generateOHSISystemMemory());
       result.setCpu(generateOHSISystemCpu());
       result.setSysLoad(generateOHSISysLoad());
       result.setOwnSelf(generateOHSIOwnSelf());
       return result;
   }

    public OSHIOwnSelf generateOHSIOwnSelf() {
        OSHIOwnSelf ownSelf = new OSHIOwnSelf();
        OSProcess process = systemInfo.getOperatingSystem().getProcess(getPid());
        ownSelf.setCpu(generateOHSICpu(process));
        ownSelf.setJvm(generateOSHIJvm(process));
        return ownSelf;
    }

    private OSHIJvm generateOSHIJvm(OSProcess process) {
        OSHIJvm jvm = new OSHIJvm();
        Runtime runtime = Runtime.getRuntime();
        //jvm总内存
        long jvmTotalMemoryByte = runtime.totalMemory();
        jvm.setJvmMemoryTotal(jvmTotalMemoryByte);
        //jvm最大可申请
        long jvmMaxMoryByte = runtime.maxMemory();
        jvm.setJvmMemoryMax(jvmMaxMoryByte);
        //空闲空间
        long freeMemoryByte = runtime.freeMemory();
        jvm.setJvmFreeMemory(freeMemoryByte);
        jvm.setBytesRead(process.getBytesRead());
        jvm.setBytesRead(process.getBytesRead());
        jvm.setBytesWritten(process.getBytesWritten());
        jvm.setVirtualSize(process.getVirtualSize());
        jvm.setResidentSetSize(process.getResidentSetSize());
        return jvm;
    }

    private OSHICpu generateOHSICpu(OSProcess process) {
        OSHICpu cpu = new OSHICpu();
        cpu.setThreadNum(process.getThreadCount());
//        double cpuPercent = Double.parseDouble(doubleToString(process.calculateCpuPercent(),3));
        double cpuPercent = PER_UNIT * (process.getKernelTime() + process.getUserTime())  / (double)process.getUpTime();
        cpu.setUsedPer(cpuPercent);
        return cpu;
    }

    private OSHISysLoad generateOHSISysLoad() {
        OSHISysLoad sysLoad = new OSHISysLoad();
        CentralProcessor centralProcessor =  systemInfo.getHardware().getProcessor();
//        sysLoad.setSysLoadAverage(Double.parseDouble(String.format(" %.3f", centralProcessor.getSystemLoadAverage(3))));
        double[] loadAverages = centralProcessor.getSystemLoadAverage(3);
        sysLoad.setSysLoadAverage(Double.parseDouble(String.format(" %.3f", loadAverages[0])));
        sysLoad.setSysLoadAverages(loadAverages);
        sysLoad.setSysCpuLoad(Double.parseDouble(String.format(" %.4f", loadAverages[0])));
        return sysLoad;
    }

    private OSHISystemCpu generateOHSISystemCpu() {
        CentralProcessor centralProcessor =  systemInfo.getHardware().getProcessor();
        OSHISystemCpu result = new OSHISystemCpu();
        result.setThreadNum(systemInfo.getOperatingSystem().getThreadCount());
        long[] prevTicks = centralProcessor.getSystemCpuLoadTicks();
        Util.sleep(OSHI_WAIT_SECOND);
        long[] ticks = centralProcessor.getSystemCpuLoadTicks();
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
        long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
        //cpu系统使用率
        double sysPer =  divide(cSys * PER_UNIT,totalCpu);
        //cpu用户使用率
        double userPer = divide(user * PER_UNIT ,totalCpu);
        //cpu当前等待率
        double iowaitPer = divide(iowait * PER_UNIT,totalCpu);
        //cpu当前使用率
        double usePer = 1.0 * PER_UNIT - divide(idle * PER_UNIT ,totalCpu);
        result.setCpuNum(centralProcessor.getLogicalProcessorCount());
        result.setUsedPer(usePer);
        result.setSysPer(sysPer);
        result.setIowaitPer(iowaitPer);
        result.setUserPer(userPer);
        return result;
    }

    /**
     * 获取系统内存对象
     * @return
     */
   private OSHIMemory generateOHSISystemMemory()
   {
       //系统内存
       GlobalMemory globalMemory = systemInfo.getHardware().getMemory();

       OSHIMemory memory = new OSHIMemory();
       memory.setTotal(globalMemory.getTotal());
       memory.setAvailable(globalMemory.getAvailable());
//       memory.setSwapTotal(globalMemory.getSwapTotal());
//       memory.setSwapUsed(globalMemory.getSwapUsed());

       memory.setSwapTotal(globalMemory.getVirtualMemory().getSwapTotal());
       memory.setSwapUsed(globalMemory.getVirtualMemory().getSwapUsed());
        long use =memory.getTotal() - memory.getAvailable();
       memory.setUsePer(divide(use * PER_UNIT,memory.getTotal()));
       return memory;
   }

    public String doubleToString(double value,int scale)
    {
        if (Double.isInfinite(value) || Double.isNaN(value))
        {
            return "0";
        }
        return String.format(" %."+scale+"f", value);
    }

   public double divide(double v1,double v2)
    {
        return divide(v1,v2,2);
    }

    public double divide(double v1,double v2,int scale)
    {
        if (Double.isInfinite(v1) || Double.isNaN(v1))
        {
         return 0;
        }
        if (v2 <= 0) return -1;
        BigDecimal bigDecimal1 = new BigDecimal(v1);
        BigDecimal bigDecimal2 = new BigDecimal(v2);
        return bigDecimal1.divide(bigDecimal2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public int getPid()
    {
        return systemInfo.getOperatingSystem().getProcessId();
    }



    public Object[][] parseProcesses() {
        SystemInfo si = systemInfo;
        List<OSProcess> list = si.getOperatingSystem().getProcesses(1, null);
        long totalMem = si.getHardware().getMemory().getTotal();
        int cpuCount = si.getHardware().getProcessor().getLogicalProcessorCount();
        // Build a map with a value for each process to control the sort
        Map<OSProcess, Double> processSortValueMap = new HashMap<>();
        for (OSProcess p : list) {
            int pid = p.getProcessID();
            // Ignore the Idle process on Windows
            if (pid > 0 || !SystemInfo.getCurrentPlatformEnum().equals(PlatformEnum.WINDOWS)) {
                // Set up for appropriate sort
                processSortValueMap.put(p, p.getProcessCpuLoadBetweenTicks(priorSnapshotMap.get(pid)));

            }
        }
        // Now sort the list by the values
        List<Map.Entry<OSProcess, Double>> procList = new ArrayList<>(processSortValueMap.entrySet());
        procList.sort(Map.Entry.comparingByValue());
        // Insert into array in reverse order (lowest sort value last)
        int i = procList.size();
        Object[][] procArr = new Object[i][COLUMNS.length];
        // These are in descending CPU order
        for (Map.Entry<OSProcess, Double> e : procList) {
            OSProcess p = e.getKey();
            // Matches order of COLUMNS field
            i--;
            int pid = p.getProcessID();
            procArr[i][0] = pid;
            procArr[i][1] = p.getParentProcessID();
            procArr[i][2] = p.getThreadCount();
            procArr[i][3] = String.format("%.1f",
                    100d * p.getProcessCpuLoadBetweenTicks(priorSnapshotMap.get(pid)));
            procArr[i][4] = String.format("%.1f", 100d * p.getProcessCpuLoadCumulative());
            procArr[i][5] = FormatUtil.formatBytes(p.getVirtualSize());
            procArr[i][6] = FormatUtil.formatBytes(p.getResidentSetSize());
            procArr[i][7] = String.format("%.1f", 100d * p.getResidentSetSize() / totalMem);
            procArr[i][8] = p.getName();
        }
        // Re-populate snapshot map
        priorSnapshotMap.clear();
        for (OSProcess p : list) {
            priorSnapshotMap.put(p.getProcessID(), p);
        }
        return procArr;
    }
    private static final String[] COLUMNS = { "PID", "PPID", "Threads", "% CPU", "Cumulative", "VSZ", "RSS", "% Memory",
            "Process Name" };
    private transient Map<Integer, OSProcess> priorSnapshotMap = new HashMap<>();


    public static void main(String[] args) throws InterruptedException {
        while(true)
        {

//            System.out.println(JSON.toJSONString(OSHIService.getInstance().parseProcesses()));
//            OSHIService.getInstance().generateOHSIOwnSelf();
            SystemInfo systemInfo = new SystemInfo();
            System.out.println(systemInfo.getOperatingSystem().getProcessId());

            System.out.println(JSON.toJSONString(systemInfo.getOperatingSystem().getProcesses()));
            Thread.sleep(1000);
        }


    }
}

